11 Glide 快速入门

项目地址:https://github.com/bumptech/glide
官方文档:http://bumptech.github.io/glide/
中文文档:https://muyangmin.github.io/glide-docs-cn/
参考链接:https://blog.csdn.net/yulyu/article/details/55053099
·
文章目录:
Glide 快速入门
Glide 源码分析执行流程
Glide 缓存机制

Glide 作为 Google 推荐的图片加载框架,它不仅性能高,功能强大,而且调用起来非常简单。

一、入门

添加依赖:

1
2
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'

添加网络权限:

1
<uses-permission android:name="android.permission.INTERNET" />

加载网络图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView iv = findViewById(R.id.iv);
String url = "https://upload-images.jianshu.io/upload_images/"
+ "14186083-93b9521d34b934cc.jpg?imageMogr2/auto-orient/"
+ "strip%7CimageView2/2/w/1240";
// 加载图片
Glide.with(this).load(url).into(iv);
}
}

加载本地图片:

1
2
3
4
// 本地文件
File file = new File(Environment.getExternalStorageDirectory(), "xxx.png");
// 加载图片
Glide.with(this).load(file).into(iv);

需要在清单文件中增加权限

1
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

二、占位图和加载动画

1、设置占位图「placeholder」
有时候加载的图片过大时,或者网络不好时,我们经常希望控件在加载过程中有一张默认的占位图。

1
Glide.with(this).load(url).placeholder(R.mipmap.place).into(iv);

问题一:当占位图比例如果跟加载出来的图比例不一致的话会导致图片拉伸变形。解决办法如下:

1
2
3
4
5
6
// 1、取消使用 place holde:
Glide.with(context).load(resId).into(imageView);
// 2、使用 place holde 加上 dontAnimate():
Glide.with(context).load(resId).placeholder(defaultId).dontAnimate().into(imageView);
// 3、使用 asBitmap 加载:
Glide.with(context).load(imageUrl).asBitmap().placeholder(defaultId).into(imageView);

问题二:如果占位图「placeHolder」比请求加载的 url 图要大,或者实际加载图是有透明部分未把占位图遮挡,就会看到占位图,占位图被当作加载成功后的图的背景展示,遇到此问题在 glide 官网查到了此问题,github 上也有这个 issiue,官网给出的对应的解决方案如下:

官网并没有明确指出具体改如何使用,本人在网上搜了各种资料也未找到具体实现。在看了 DrawableCrossFadeFactory 的源码后本人找到了答案,示例代码如下:

1
2
3
4
5
6
7
DrawableCrossFadeFactory drawableCrossFadeFactory =
new DrawableCrossFadeFactory.Builder(300).setCrossFadeEnabled(true).build();
Glide.with(this).load(URL_JPG)
.apply(new RequestOptions().placeholder(R.drawable.ic_launcher))
.transition(DrawableTransitionOptions.with(drawableCrossFadeFactory))
.into(imageView);

问题三:尽量避免 ImageView 控件的宽高设置 wrap_content,例如:

1
2
3
4
5
<ImageView
android:id="@+id/iv_gif"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>

2、设置错误图片「error」
当图片链接地址有误或者网络不行的时候,我们需要显示一个错误图片:

1
Glide.with(this).load(url).error(R.mipmap.icon_photo_error).into(iv);

3、设置动画「crossFade」
Glide 默认是包含淡入淡出动画的时间为 300ms,我们可以修改这个动画的时间:

1
Glide.with(this).load(url).placeholder(R.mipmap.place).crossFade(5000).into(iv);

4、取消动画「dontAnimate」
当我们不希望有淡入淡出动画时:

1
Glide.with(this).load(url).placeholder(R.mipmap.place).dontAnimate().into(iv);

三、加载 GIF

1、如果只是简单加载 gif,其实跟加载普通图片一样:

1
Glide.with(this).load(mGifUrl).placeholder(R.mipmap.place).error(R.mipmap.icon_error).into(mIv);

2、把 gif 当作普通图片加载「asBitmap」
如果希望加载 gif 时只加载 gif 的第一帧,把 gif 当作普通图片一样加载,那么只需要加上 asBitmap 方法即可:

1
Glide.with(this).load(mGifUrl).asBitmap().placeholder(R.mipmap.place).error(R.mipmap.icon_error).into(mIv);

3、检查是否 gif「asGif」
如果你希望加载的只是 gif,如果不是 gif 就显示错误图片,那么只用加上 asGif 方法即可:

1
Glide.with(this).load(mGifUrl).asGif().placeholder(R.mipmap.place).error(R.mipmap.icon_error).into(mIv);

4、加载本地视频缩略图
网络上很多文章上都是从一篇译文里面拷贝过来的,里面说 Glide 可以加载本地视频,但是那篇译文漏翻译一句了,Glide 只会加载本地视频的第一帧,也就是缩略图,而且其实加载缩略图的时候也无需转化为 Uri,直接把 File 丢进去就行了:

1
2
File mVideoFile = new File(Environment.getExternalStorageDirectory(), "xxx.mp4");
Glide.with(this).load(mVideoFile).placeholder(R.mipmap.place).error(R.mipmap.icon_error).into(mIv);

5、其他
在大多数情况下,当你使用 diskCacheStrategy(DiskCacheStrategy.SOURCE)时,Gif 的加载速度会显着提高(其实就是把 gif 资源缓存到磁盘)。

1
2
Glide.with(this).load(mGifUrl).diskCacheStrategy(DiskCacheStrategy.SOURCE)
.placeholder(R.mipmap.place).error(R.mipmap.icon_error).into(mIv);

四、绑定声明周期

其实 Glide 与 Activity 和 Fragment 绑定生命周期很简单,只用在 with 的时候传入想绑定生命周期的 Context 就行。在 Glide 内部会根据你 Context 的实际类型做不同的处理,具体的分析会在以后的源码分析中展示。

1
Glide.with(this).load(mUrl).into(mIv);

五、内存缓存和磁盘缓存

1、缓存的资源
Glide 的缓存资源分为两种:
· 原图(SOURCE):原始图片。
· 处理图(RESULT):经过压缩和变形等处理后的图片。

2、内存缓存策略「skipMemoryCache」
Glide 默认是会在内存中缓存处理图(RESULT)的。可以通过调用 skipMemoryCache(true) 来设置跳过内存缓存。

1
2
// 跳过内存缓存
Glide.with(this).load(mUrl).skipMemoryCache(true).into(mIv);

调用 skipMemoryCache(false) 没有代码上的意义,因为 Glide 默认就是不跳过内存缓存的,但是显示调用这个方法,可以让别人一目了然的知道你这次请求是会在内存中缓存的,所以还是建议显示调用一下这个方法来表明你的内存缓存策略。

3、磁盘缓存策略「diskCacheStrategy」
Glide 磁盘缓存策略分为四种,默认的是 RESULT:
· ALL:缓存原图(SOURCE)和处理图(RESULT)。
· NONE:什么都不缓存。
· SOURCE:只缓存原图(SOURCE)。
· RESULT:只缓存处理图(RESULT),默认值。

4、组合策略
和其他三级缓存一样,Glide 的缓存读取顺序是:内存 >> 磁盘 >> 网络。需要注意的是 Glide 的内存缓存和磁盘缓存的配置相互没有直接影响,所以可以同时进行配置。例:

1
2
3
4
// 内存不缓存,磁盘缓存缓存所有图片
Glide.with(this).load(mUrl).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.ALL).into(mIv);
// 内存缓存处理图,磁盘缓存原图
Glide.with(this).load(mUrl).skipMemoryCache(false).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(mIv);

5、缓存大小及路径
Glide 的内存缓存其实涉及到比较多的计算,这里就介绍最重要的一个参数,就是内存缓存最大空间:每个进程可用的最大内存 0.4,低配手机的话是: 每个进程可用的最大内存 0.33。

磁盘缓存大小:250 1024 1024(250MB)

1
2
/** 250 MB of cache. */
int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;

磁盘缓存目录:项目/cache/image_manager_disk_cache

1
String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache";

6、清除缓存
清除所有内存缓存(需要在 UI 线程操作)

1
Glide.get(this).clearMemory();

清除所有磁盘缓存(需要在子线程操作)

1
Glide.get(MainActivity.this).clearDiskCache();

注:在使用中的资源不会被清除。

六、通过 Modules 定制 Glide

我们一般情况下使用 Glide 都很简单,只用简单的调用几个方法就能够很好的显示图片了,但其实 Glide 在初始化的时候进行了一系列的默认配置,比如缓存的配置,图片质量的配置等等。接下来我们就介绍一下一个比较高级的功能,通过 Modules 定制自己的个性 Glide。

1、创建一个类实现 GlideModule

1
2
3
4
5
6
7
8
9
10
public class XGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// TODO
}
@Override
public void registerComponents(Context context, Glide glide) {
// TODO
}
}

2、配置清单文件
在 AndroidManifest 中配置刚刚创建的 GlideModule,需要在 Application 节点下添加:

1
2
3
4
5
6
7
<application>
...
<meta-data
android:name="xian.xiao.tao.glide.XGlideModule"
android:value="GlideModule" />
</application>

其中 android:name 就是刚才创建的 GlideModule 的实现类。

3、进行自定义配置
创建的 GlideModule 的实现类时,需要要实现两个方法,这里要用到的是其中的 applyOptions 方法,applyOptions 方法里面提供了一个 GlideBuilder,通过 GlideBuilder 实现自定义配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class XGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setDiskCache(); // 自定义磁盘缓存
builder.setMemoryCache(); // 自定义内存缓存
builder.setBitmapPool(); // 自定义图片池
builder.setDiskCacheService(); // 自定义本地缓存的线程池
builder.setResizeService(); // 自定义核心处理的线程池
builder.setDecodeFormat(); // 自定义图片质量
}
@Override
public void registerComponents(Context context, Glide glide) {
// TODO
}
}

4、示例「配置默认图片质量」
由于 Glide 的配置涉及到的东西比较多,在以后的文章里面会对每一种配置做说明具体介绍,这里就先示范一个比较简单的配置,那就是图片质量配置。

用过 Picasso 的朋友应该知道,Picasso 默认的图片质量是 ARGB_8888,而 Glide 默认的图片质量是 RGB_565。这里我们就来修改默认配置,让 Glide 的默认质量为 ARGB_8888。(ARGB_8888 是指 32 位图,即每个像素占 4byte;RGB_565 是 16 位图,即每个像素占 2byte。)

1
2
3
4
5
6
7
8
9
10
11
public class XGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 自定义图片质量
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
@Override
public void registerComponents(Context context, Glide glide) {
// TODO
}
}

5、其他
在清单文件中可以配置多个 GlideModule,Glide 会依次遍历并读取。

七、自定义缓存

1、自定义内存缓存

通过调用 builder 的 setXXX 方法就可以自定义内存缓存的大小,MemoryCache 以及 BitmapPool 都与内存缓存有关。

1
2
3
4
5
6
7
8
9
10
11
12
public class XGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 配置内存缓存大小 10MB
builder.setMemoryCache(new LruResourceCache(10*1024*1024));
// 配置图片池大小 20MB
builder.setBitmapPool(new LruBitmapPool(20*1024*1024));
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}

但是内存缓存的大小数值其实不应该是随便配置的,Glide 的内部的默认值是通过一系列的计算获取的,比如说判断手机是否高配置手机等。有个取巧的办法,就是获取 Glide 默认的设置,然后在这个设置的基础上进行修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class XGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 获取内存计算器
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
// 获取Glide默认内存缓存大小
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
// 获取Glide默认图片池大小
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();
// 将数值修改为之前的1.1倍
int myMemoryCacheSize = (int) (1.1 * defaultMemoryCacheSize);
int myBitmapPoolSize = (int) (1.1 * defaultBitmapPoolSize);
// 修改默认值
builder.setMemoryCache(new LruResourceCache(myMemoryCacheSize));
builder.setBitmapPool(new LruBitmapPool(myBitmapPoolSize));
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}

2、自定义磁盘缓存

磁盘缓存按照访问权限及路径可以分为两种:
· 私有缓存(只有 App 本身可以访问,目录在 “data/data/应用包名” 下)
· 外部缓存(所有 App 都可以访问,目录在扩展空间内,如 SD 卡)

2.1、私有缓存

下面这样配置的话,主要是修改缓存大小,缓存路径仍为默认路径(data/data/应用包名/cache/image_manager_disk_cache)

1
2
3
4
5
6
7
8
9
10
11
12
public class XGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 设置磁盘缓存大小
int size = 100 * 1024 * 1024;
// 设置磁盘缓存
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, size));
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}

如果需求修改缓存路径的话,需要增加一下参数即可(变为data/data/应用包名/cache/xian)。

1
2
3
4
5
// 设置磁盘缓存大小
int size = 100 * 1024 * 1024;
String dir = "xian";
// 设置磁盘缓存
builder.setDiskCache(new InternalCacheDiskCacheFactory(context,dir, size));

2.2、外部缓存

下面这样配置的话,缓存会在 SDCard/Android/data/应用包名/cache/image_manager_disk_cache 目录下:

1
2
3
4
5
6
7
8
9
10
11
12
public class XGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 设置磁盘缓存大小
int size = 100 * 1024 * 1024;
// 设置磁盘缓存
builder.setDiskCache(new ExternalCacheDiskCacheFactory(context, size));
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}

如果需求修改缓存路径的话,需要增加一下参数即可(变为 SDCard/Android/data/应用包名/cache/xian)

1
2
3
4
5
// 设置磁盘缓存大小
int size = 100 * 1024 * 1024;
String dir = "xian";
// 设置磁盘缓存
builder.setDiskCache(new ExternalCacheDiskCacheFactory(context,dir, size));

2.3、完全自定义路径

上面两种缓存都是把缓存的父目录定死的,能够改变的只是子目录。Glide 还提供了一种方式可以完全自定义缓存目录。需要注意的是这里的路径是配置的绝对路径,所以如果没有指定在 sd 卡目录下的话是无法直接看到的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class XiayuGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 设置磁盘缓存大小
int size = 100 * 1024 * 1024;
String dirFolder = "xian";
String dirName = "xiao";
// 设置磁盘缓存
builder.setDiskCache(new DiskLruCacheFactory(dirFolder, size));
// builder.setDiskCache(new DiskLruCacheFactory(dirFolder, dirName,size));
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}

八、图片压缩

1、图片质量的压缩或者提高
见上面 6.2 节,通过 Modules 定制 Glide。

2、图片尺寸的压缩或者拉伸「override」
通过调用 override,就可以把图片压缩到相应的尺寸来显示。类似这些被处理过的图片,就是前文提到的 RESULT(处理图)

1
Glide.with(this).load(mUrl).override(300,300).into(mIv);

九、图片预处理(圆角高斯模糊等)

1、创建一个类继承 BitmapTransformation
需要实现两个方法,其中 transform 方法里面能拿到 bitmap 对象,这里就是对图片做处理的地方。

1
2
3
4
5
6
7
8
9
10
public class CornersTransform extends BitmapTransformation {
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
// TODO
}
@Override
public String getId() {
// TODO
}
}

2、使用
通过调用 transform 方法就能展示处理后的图片

1
Glide.with(this).load(url).transform(new CornersTransform()).into(iv1);

3、示例「圆角处理」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class CornersTransform extends BitmapTransformation {
private float radius;
public CornersTransform(Context context) {
super(context);
radius = 10;
}
public CornersTransform(Context context, float radius) {
super(context);
this.radius = radius;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return cornersCrop(pool, toTransform);
}
private Bitmap cornersCrop(BitmapPool pool, Bitmap source) {
if (source == null) return null;
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rectF, radius, radius, paint);
return result;
}
@Override
public String getId() {
return getClass().getName();
}
}

使用

1
Glide.with(this).load(url).transform(new CornersTransform(this,50)).into(iv1);

4、使用多个 transform
transform 方法是不支持多次调用,如果被调用两次,那么第二次的会覆盖了第一次的效果。但是他有一个重载的方法可以传入多个对象,这样传入的变形器都能够生效。

1
Glide.with(this).load(url).transform(new CircleTransform(this), new CornersTransform(this,50)).into(iv1);

5、第三方库
如果你觉得自己自定义 transform 比较困难,或者你想学习别人的图片处理方法,可以在试一试 Github 上的这个库:Glide Transformations

其效果如下,支持圆角、高斯模糊等。

十、图片裁剪

ImageView 的 ScaleType

ImageView 的 ScaleType 一共有 8 种属性:

  • matrix:不缩放,图片与控件左上角对齐,当图片大小超过控件时将被裁剪。
  • center:不缩放,图片与控件中心点对齐,当图片大小超过控件时将被裁剪。
  • centerInside:以完整显示图片为目标,不剪裁,当显示不下的时候将缩放,能够显示的情况下不缩放。
  • centerCrop:以填满整个控件为目标,等比缩放,超过控件时将被 裁剪(宽高都要填满,所以只要图片宽高比与控件宽高比不同时,一定会被剪裁)。
  • fitCenter:默认,自适应控件,不剪裁,在不超过控件的前提下,等比缩放到最大,居中显示。
  • fitStart:自适应控件,不剪裁,在不超过控件的前提下,等比缩放到最大,靠左(上)显示。
  • fitEnd:自适应控件,不剪裁,在不超过控件的前提下,等比缩放到最大,靠右(下)显示。
  • fitXY:以填满整个控件为目标,不按比例拉伸或缩放(可能会变形),不剪裁。

Glide 有两个方法可以设置图片剪裁的策略:
· fitCenter()
· centerCrop()

这两个方法其实都是通过调用 transform 方法来对图片进行处理。当你没有调用上述两个方法,并且也没有调用 transform 方法的时候,在 Glide 调用 into 方法时,会根据你设置的 ScaleType 来做处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
default:
// Do nothing.
}
}